package ia;

import java.util.ArrayList;
import java.util.Collections;


public class Nodo90 extends Nodo implements Comparable<Nodo90>{
  
	private Nodo90            _padre;
    private ArrayList<Nodo90> _hijos = new ArrayList<Nodo90>();

    public Nodo90(int x, int y, Orientacion o){
        super(x,y,o);        
    	_padre=null;
    }

    public void setPadre(Nodo nodoPadre){
        this._padre = (Nodo90)nodoPadre;
    }

    public ArrayList<Nodo90> getHijos(){
        return _hijos;
    }

    public Nodo90 getPadre(){
        return _padre;
    }
    
    // obtiene los antecesores de un nodo
    public ArrayList<Nodo90> getAntecesores(){
        ArrayList<Nodo90> antecesores = new ArrayList<Nodo90>();
        Nodo90 padre = this.getPadre();
        while(padre!=null){
            antecesores.add(padre);
            padre = padre.getPadre();
        }
        return antecesores;
    }
    
    // obtiene la profundidad de un nodo
    public int getProfundidad(){
        int profundidad=0;
        Nodo90 padre = this.getPadre();
        while(padre!=null){
            profundidad++;
            padre = padre.getPadre();
        }
        return profundidad;
    }

    // indica si es posible moverse en la direccion
    public boolean puedeMover(int[][] matriz){
        int ultimaFila    = matriz.length-1;
        int ultimaColumna = matriz[0].length-1;
        
        if ((_or == Orientacion.N) & (_x == 0))               return false;
        if ((_or == Orientacion.E) & (_y == ultimaColumna))   return false;
        if ((_or == Orientacion.S) & (_x == ultimaFila))      return false;
        if ((_or == Orientacion.O) & (_y == 0))               return false;
        
        return true;
    }

    public Nodo90 mover(int[][]matriz, Orientacion orientacion){
        Nodo90 nodo = null;
        int fila    = 0;
        int columna = 0;        
        
        if (orientacion == Orientacion.N) nodo = new Nodo90(fila=_x-1,columna=_y,Orientacion.N); 
        if (orientacion == Orientacion.O) nodo = new Nodo90(fila=_x,columna=_y-1,Orientacion.O);
        if (orientacion == Orientacion.S) nodo = new Nodo90(fila=_x+1,columna=_y,Orientacion.S); 
        if (orientacion == Orientacion.E) nodo = new Nodo90(fila=_x,columna=_y+1,Orientacion.E); 

        nodo.setTransicionCoste(matriz[fila][columna]);
        nodo.setCoste(_coste+nodo.getTransicionCoste());
        
        _hijos.add(nodo);
        return nodo;
    }
    
    public ArrayList<Nodo90> aplicaOperadores(int[][] matrizPeligrosidades){
       ArrayList<Nodo90> sucesores = new ArrayList<Nodo90>();
       Nodo90 giroIzq;  // Nodo girando en sentido antihorario
       Nodo90 giroDer;  // Nodo girando en sentido horario
       boolean posibleMover = this.puedeMover(matrizPeligrosidades);
       float costePadre = _coste;

       if (_or == Orientacion.N) {
   				giroIzq = iniciarNodo(Orientacion.O,costePadre);
   				giroDer = iniciarNodo(Orientacion.E,costePadre);
   				sucesores.add(giroIzq);
   				sucesores.add(giroDer);
                if (posibleMover) sucesores.add(mover(matrizPeligrosidades,Orientacion.N));
       }
       if (_or == Orientacion.S) {
   				giroIzq = iniciarNodo(Orientacion.E,costePadre);
   				giroDer = iniciarNodo(Orientacion.O,costePadre);
   				sucesores.add(giroIzq);
   				sucesores.add(giroDer);
                if (posibleMover) sucesores.add(mover(matrizPeligrosidades,Orientacion.S));
       }
       if (_or == Orientacion.E) {
   				giroIzq = iniciarNodo(Orientacion.N,costePadre);
   				giroDer = iniciarNodo(Orientacion.S,costePadre);
   				sucesores.add(giroIzq);
   				sucesores.add(giroDer);
                if (posibleMover) sucesores.add(mover(matrizPeligrosidades,Orientacion.E));
       }
       if (_or == Orientacion.O) {
   				giroIzq = iniciarNodo(Orientacion.S,costePadre);
   				giroDer = iniciarNodo(Orientacion.N,costePadre);
   				sucesores.add(giroIzq);
   				sucesores.add(giroDer);	   
                if (posibleMover) sucesores.add(mover(matrizPeligrosidades,Orientacion.O));
       }
      
      _hijos.addAll(sucesores);
      return sucesores;
    }

    // crea un nuevo nodo
    private Nodo90 iniciarNodo(Orientacion or,float costePadre) {
    	
    	Nodo90 aux = new Nodo90(_x,_y,or);
    	aux.setCoste(costePadre+1);
    	aux.setTransicionCoste(1);
    	
    	return aux;
    }


    public void mostrarResultado(ArrayList<Nodo90> cerrados){
    	
        if (cerrados!=null) {
            ArrayList<Nodo90> recorrido = new ArrayList<Nodo90>();
            recorrido.addAll(this.getAntecesores());
            Collections.reverse(recorrido); // el recorrido se encuentra al revés
            recorrido.add(this);            // añadimos el nodo actual
            float mejorCoste = this.getCoste();
            System.out.println("Nodo final:                   "+this);
            System.out.println("Recorrido:                    "+recorrido);
            recorrido.clear();
            int profundidad = this.getProfundidad();
            System.out.println("Profundidad:                  "+profundidad);
            System.out.println("Coste:                        "+mejorCoste);
            System.out.println("Factor de ramificación:       "+BranchingFactor.compute(cerrados.size(), profundidad));	
            System.out.println("Nodos cerrados seleccionados: "+cerrados.size());
    	
        }
        else
        	System.out.println("La solucion del problema es el nodo principal");
    }
    
    
    @Override
    public boolean equals(Object nodo){
        if(nodo instanceof Nodo90){
            Nodo90 n = (Nodo90)nodo;
            if((n.getX() == _x) & (n.getY() == _y) & (n.getOrientacion() == _or)) return true;
            else 
            	 return false;
        }else 
        	 return false;
    }

    @Override
    public int compareTo(Nodo90  nodo) {
        if(this.getFinal() < nodo.getFinal())       return -1;
        else if(this.getFinal() > nodo.getFinal())  return  1;
        else return 0;
    }
    
    @Override
    public String toString(){
        String anchura   = "("+_x+","+_y+","+_or+")";
        String Aestrella = "ID:"+_id+"("+_x+","+_y+","+_or+")"
                +" g="+_coste
                +" h="+_valorH;
        
        if (_valorH < 0) 
        	return anchura;
        else 
        	return Aestrella;
    }
}
